package com.ziytek.web.citizen.handler.client;

import com.google.gson.Gson;
import com.ziytek.web.citizen.caffeine.CaffeinePut;
import com.ziytek.web.citizen.config.ConfigInit;
import com.ziytek.web.citizen.config.caffeine.CaffeineUtil;
import com.ziytek.web.citizen.constant.NumConstant;
import com.ziytek.web.citizen.model.bean.OfflineInfo;
import com.ziytek.web.citizen.model.tcp.req.TcpBatteryDetailReq;
import com.ziytek.web.citizen.model.tcp.req.TcpRealTimeReq;
import com.ziytek.web.citizen.model.tcp.req.TcpStatusReq;
import com.ziytek.web.citizen.service.ClientBatteryDetailReqService;
import com.ziytek.web.citizen.service.WebClientService;
import com.ziytek.web.citizen.util.DateTimeUtils;
import com.ziytek.web.citizen.util.SpringBeanUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

import java.net.InetSocketAddress;

/**
 * @author fengh
 */
@Slf4j
public class WsWebSocketHandler extends SimpleChannelInboundHandler<WebSocketFrame> {


    private static final Gson GSON = new Gson();
    private static final ConfigInit CONFIG_INIT = SpringBeanUtils.getApplicationContext().getBean(ConfigInit.class);
    private static final CaffeinePut CAFFEINE_PUT = SpringBeanUtils.getApplicationContext().getBean(CaffeinePut.class);
    private static final WebClientService WEB_CLIENT_SERVICE = SpringBeanUtils.getApplicationContext().getBean(WebClientService.class);
    private static final ClientBatteryDetailReqService REQ_SERVICE = SpringBeanUtils.getApplicationContext().getBean(ClientBatteryDetailReqService.class);

    private static Long SCHEDULED_TIME_DELAY = NumConstant.LONG_ZERO;

    /**
     * TCP客户端上送数据计数缓存
     */
    private void putReadMap(String clientIp, ChannelHandlerContext ctx) {
        if (ctx == null) {
            return;
        }

        //log.info("set putReadMap {}", clientIp);
        CaffeineUtil.set(CaffeineUtil.WEB_IP_CACHE_PREFIX + clientIp, ctx, NumConstant.INT_SIXTY);
    }

    private Long getReadMap(String clientIp, ChannelHandlerContext ctx) {
        ChannelHandlerContext channelHandlerContext = CaffeineUtil.get(CaffeineUtil.WEB_IP_CACHE_PREFIX + clientIp);
        //log.info("get getReadMap {}", clientIp);
        if (ctx != null && channelHandlerContext != null) {
            return getDelayTime();
        }
        return NumConstant.LONG_ZERO;
    }

    private long getDelayTime() {
        if (SCHEDULED_TIME_DELAY <= NumConstant.LONG_ZERO) {
            long scheduledTimeDelay = CONFIG_INIT.getScheduledTimeDelay();
            SCHEDULED_TIME_DELAY = scheduledTimeDelay <= NumConstant.LONG_ZERO ? NumConstant.INT_SIXTY : scheduledTimeDelay;
            return SCHEDULED_TIME_DELAY;
        }
        return SCHEDULED_TIME_DELAY;
    }

    private String getClientIp(ChannelHandlerContext ctx) {
        return ctx == null ? null : ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
    }

    /**
     * 通道就绪后触发，一般用于初始化
     *
     * @param ctx ctx
     * @throws Exception e
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //log.info("有新的连接加入...");
        CaffeineUtil.delete(CaffeineUtil.WEB_KEY_OFFLINE + getClientIp(ctx));
        log.info("前端客户端建立连接 web client Execute during registration--channelRegistered--id:{},client IP:{}", ctx.channel().id().toString(), getClientIp(ctx));

        var clientIp = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
        var port = ((InetSocketAddress) ctx.channel().remoteAddress()).getPort();
        putReadMap(clientIp + "_" + port, ctx);
        // 保存柜子客户端连接IP地址信息缓存
        CAFFEINE_PUT.putChannelContext(clientIp + "_" + port, ctx);
    }

    /**
     * 通道关闭后触发，一般用于释放资源
     *
     * @param ctx c
     * @throws Exception e
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        var offlineInfo = new OfflineInfo();
        offlineInfo.setOfflineTime(DateTimeUtils.toDate(System.currentTimeMillis(), null));
        offlineInfo.setOfflineReason("web client  channelUnregistered");
        CaffeineUtil.set(CaffeineUtil.WEB_KEY_OFFLINE + getClientIp(ctx), offlineInfo, 60);
        super.channelUnregistered(ctx);

        WEB_CLIENT_SERVICE.deleteCacheStats(ctx);
        WEB_CLIENT_SERVICE.deleteCacheRealTime(ctx);
        WEB_CLIENT_SERVICE.deleteCacheStatsFirst(ctx);

        log.info("前端客户端离线 web client offlineReason--channelUnregistered--id:{},client IP:{}", ctx.channel().id().toString(), getClientIp(ctx));
        ctx.channel().close();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        // 认证
        if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete e) {
            log.info("前端客户端与后台服务端握手成功,uri:{}", e.requestUri());
        }
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame webSocketFrame) throws Exception {
        var clientIp = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
        var port = ((InetSocketAddress) ctx.channel().remoteAddress()).getPort();
        putReadMap(clientIp + "_" + port, ctx);

        String text = "";
        if (webSocketFrame instanceof TextWebSocketFrame t) {
            text = ((TextWebSocketFrame) webSocketFrame).text();
        } else if (webSocketFrame instanceof BinaryWebSocketFrame) {
            ByteBuf data = webSocketFrame.content();
            // 处理二进制数据...
        }

        Assert.notNull(text, "接受到前端客户端的消息 web client  send msg is null");

        log.info("接受到前端客户端的消息 web client channelRead msg:{}", text);
        try {

            if (GSON.fromJson(text, TcpBatteryDetailReq.class) instanceof TcpBatteryDetailReq rq
                    && ClientBatteryDetailReqService.TOPIC_REAL_STATS.equalsIgnoreCase(rq.getTopic()) && StringUtils.isNotEmpty(rq.getCabinetNo())) {
                REQ_SERVICE.batteryDetailReq(rq, ctx);
                return;
            }

            if (GSON.fromJson(text, TcpRealTimeReq.class) instanceof TcpRealTimeReq rdq
                    && ClientBatteryDetailReqService.TOPIC_REALTIME.equalsIgnoreCase(rdq.getTopic()) && StringUtils.isNotEmpty(rdq.getCabinetNo())) {
                REQ_SERVICE.realTimeReq(rdq, ctx);
            }

            if (GSON.fromJson(text, TcpStatusReq.class) instanceof TcpStatusReq rdq
                    && ClientBatteryDetailReqService.TOPIC_STATUS.equalsIgnoreCase(rdq.getTopic())) {
                REQ_SERVICE.cabinetStatus(rdq, ctx);
            }
        } catch (Exception e) {
            log.info("接受到前端客户端的消息异常 web client channelRead error:", e);
        } finally {
            // 解决客户端频繁掉线问题
            ctx.flush();
        }
    }
}
